#include <assert.h>
#include "SplitterWnd.h"

#define DEFAULT_BAR_WIDTH 3

CSplitterWindow::CSplitterWindow()
{
    m_pPanes[0] = NULL;
    m_pPanes[1] = NULL;
    m_uPane1Span = -1;
    m_direction = EastWest;
    m_uBarWidth = DEFAULT_BAR_WIDTH;
    m_bSizing = false;
    m_pen = NULL;
}

CSplitterWindow::~CSplitterWindow()
{
}

void CSplitterWindow::SetParameters(BarDirection direction, 
                IPaneWindow* pPane1, 
                IPaneWindow* pPane2, 
                UINT uPane1Span)
{
    assert (pPane1 && pPane2);
    m_pPanes[0] = pPane1;
    m_pPanes[1] = pPane2;
    m_uPane1Span = uPane1Span;
    m_direction = direction;
    m_uBarWidth = DEFAULT_BAR_WIDTH;
    m_hCursor = NULL;
}

void CSplitterWindow::SetBarPos(UINT pos)
{
    m_uPane1Span = pos;
    MovePanes();
}

HWND CSplitterWindow::CreateSplitterWnd (HWND hParent, RECT& rc)
{
    assert (hParent);
    assert (m_pPanes[0] && m_pPanes[1]);
    assert (m_uPane1Span >= 0);

    DWORD dwExStyle =  0;
    DWORD dwStyle =  WS_CHILD | WS_VISIBLE;

    m_hParent = hParent;
    m_hWnd = Create (hParent, rc, NULL, dwStyle, dwExStyle);
    m_pen = CreatePen (PS_SOLID, m_uBarWidth, RGB(0,0,0));

    if (!m_hWnd) {
        DWORD err = GetLastError();
        return NULL;
    }
    ::ShowWindow(m_hWnd, SW_SHOW);
    ::UpdateWindow(m_hWnd);
    return m_hWnd;
}

void  CSplitterWindow::SetBarWidth (UINT w)
{
    m_uBarWidth = w;
}

LRESULT CSplitterWindow::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    int i = 0;
    RECT rcClient;

    LPCSTR pCurName = (m_direction == EastWest ? IDC_SIZEWE : IDC_SIZENS);
    // Cursor hovers over.
    m_hCursor = LoadCursor(NULL, pCurName);

    GetClientRect(&rcClient);
    UINT nSpan = ((m_direction == EastWest) ? 
                 (rcClient.right - rcClient.left) : 
                 (rcClient.bottom - rcClient.top));
    m_uPane1Span = ((m_uPane1Span + m_uBarWidth > nSpan) ? 
                    (nSpan - m_uBarWidth) :
                    m_uPane1Span);
    RECT rcPane[2];
    SetPaneRects(rcPane);
    for (i = 0; i < 2; i++) {
        m_hPanes[i] = m_pPanes[i]->CreatePaneWindow (m_hWnd, rcPane[i], 0, 0);
    }
   
	return 0;
}

LRESULT CSplitterWindow::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    if (m_pen) {
        DeleteObject((HGDIOBJ)m_pen);
    }
	return 0;
}

LRESULT CSplitterWindow::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    Invalidate(FALSE);
    MovePanes();
    return 0;
}

LRESULT CSplitterWindow::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    assert (m_hCursor);

    SetCursor(m_hCursor);
    if (!m_bSizing) {
        return 0;
    }

    RECT rc;
    GetClientRect(&rc);
    DrawSizingBar (m_uSizingPos);
    int nPos;
    if (m_direction == EastWest) {
        nPos = (short)(LOWORD(lParam));
        if (nPos < 0) {
            nPos = 0;
        } else if (nPos > (int)(rc.right - m_uBarWidth)) {
            nPos = rc.right - m_uBarWidth;
        }
    } else {
        nPos = (short)(HIWORD(lParam));
        if (nPos < 0) {
            nPos = 0;
        } else if (nPos > (int)(rc.bottom - m_uBarWidth)) {
            nPos = rc.bottom - m_uBarWidth;
        }
    }
    m_uSizingPos = nPos;
    DrawSizingBar(nPos);
    return 0;
}

LRESULT CSplitterWindow::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_bSizing = true;

    if (m_direction == EastWest) {
        DrawSizingBar(LOWORD(lParam));
        m_uSizingPos = LOWORD(lParam);
    } else {
        DrawSizingBar(HIWORD(lParam));
        m_uSizingPos = HIWORD(lParam);
    }
    SetCapture();
    return 0;
}

LRESULT CSplitterWindow::OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    m_bSizing = false;
    ReleaseCapture();
    
    DrawSizingBar(m_uSizingPos);
    m_uPane1Span = m_uSizingPos;
    MovePanes();
    return 0;
}


void CSplitterWindow::SetPaneRects(RECT rcPane[])
{
    RECT rcClient;
    assert (rcPane);    

    GetClientRect(&rcClient);
    if (m_direction == EastWest) {
        rcPane[0].top = rcPane[1].top = rcClient.top;
        rcPane[0].bottom = rcPane[1].bottom = rcClient.bottom;
        rcPane[0].left = rcClient.left;
        rcPane[0].right = rcClient.left + m_uPane1Span;
        rcPane[1].left = rcPane[0].right + m_uBarWidth;
        rcPane[1].right = rcClient.right;
    } else {
        rcPane[0].left = rcPane[1].left = rcClient.left;
        rcPane[0].right = rcPane[1].right = rcClient.right;
        rcPane[0].top = rcClient.top;
        rcPane[0].bottom = rcClient.top + m_uPane1Span;
        rcPane[1].top = rcPane[0].bottom + m_uBarWidth;
        rcPane[1].bottom = rcClient.bottom;
    }
}

void CSplitterWindow::DrawSizingBar(UINT uPos)
{
	HDC hDC = GetDC();
	int iOldROP2 = ::SetROP2(hDC, R2_NOT);
	RECT rcClient;

	GetClientRect(&rcClient);
	HPEN hOldPen = (HPEN)::SelectObject(hDC, m_pen);
	if (m_direction == EastWest) {
        MoveToEx(hDC, uPos, rcClient.top, NULL);
        LineTo(hDC, uPos, rcClient.bottom);
    } else {
        MoveToEx(hDC, rcClient.left, uPos, NULL);
        LineTo(hDC, rcClient.right, uPos);
	}
	::SetROP2(hDC, iOldROP2);
	::SelectObject(hDC, hOldPen);
	ReleaseDC(hDC);
}

void CSplitterWindow::MovePanes()
{
    RECT rc;
    GetClientRect(&rc);

    if (m_direction == EastWest) {
        ::MoveWindow(m_hPanes[0], 0, 0, m_uPane1Span, rc.bottom, TRUE);
        ::MoveWindow(m_hPanes[1], m_uPane1Span+m_uBarWidth, 0, 
                     rc.right - m_uPane1Span-m_uBarWidth,
                     rc.bottom,
                     TRUE);
    } else {
        ::MoveWindow(m_hPanes[0], 0, 0, rc.right, m_uPane1Span, TRUE);
        ::MoveWindow(m_hPanes[1], 0, m_uPane1Span+m_uBarWidth, 
                     rc.right, 
                     rc.bottom - m_uPane1Span-m_uBarWidth, 
                     TRUE);
    }
    ::InvalidateRect(m_hPanes[0], NULL, FALSE);
    ::InvalidateRect(m_hPanes[1], NULL, FALSE);
}
